﻿using Blm.Utils.Extensions;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using VisionCloud.Utility.Security;

namespace VisionCloud.Utility.Redis
{
    /// <summary>
    /// 慎用该类的所有异步方法，经常出Bug
    /// </summary>
    public class RedisHelper
    {
        public static RedisHelper Instance { get; private set; }

        static RedisHelper()
        {
            GetInstance();
        }

        private static readonly object ObjectLocker = new object();
        private static readonly string Mode = ConfigHelper.GetValue<string>("RedisSetting:RedisMode") ?? string.Empty;
        private static readonly string Address = ConfigHelper.GetValue<string>("RedisSetting:RedisServer") ?? "127.0.0.1:6379";
        private static readonly int RedisDb = ConfigHelper.GetValue<string>("RedisSetting:RedisDb").ToInt32();
        private static readonly string RedisServerName = ConfigHelper.GetValue<string>("RedisSetting:RedisServerName") ?? string.Empty;
        private static readonly string RedisPassword = AesCoreHelper.Decrypt(ConfigHelper.GetValue<string>("RedisSetting:RedisPassword"), AesCoreHelper.Sceret);

        #region 创建实例

        /// <summary>
        /// Redis访问单例：未配置RedisServer时默认访问本机6379
        /// </summary>
        /// <returns></returns>
        public static RedisHelper GetInstance()
        {
            if (Instance == null)
            {
                lock (ObjectLocker)
                {
                    if (Instance == null)
                    {
                        Instance = new RedisHelper(Address);
                    }
                }
            }
            return Instance;
        }

        #endregion 创建实例

        /// <summary>
        /// 获取通用缓存时间
        /// </summary>
        /// <returns></returns>
        public static TimeSpan GetCommonTimeSpan()
        {
            return TimeSpan.FromDays(1);
        }

        private ConnectionMultiplexer Redis { get; set; }

        private IDatabase Db { get; set; }
        private IServer Server { get; set; }

        public EndPoint[] GetEndPoints()
        {
            return Redis.GetEndPoints();
        }

        public RedisHelper(string connection)
        {
            //var config = new ConfigurationOptions
            //{
            //    AbortOnConnectFail = false,
            //    AllowAdmin = true,
            //    ConnectTimeout = 15000,
            //    SyncTimeout = 15000,
            //    Ssl = false,
            //    //ResponseTimeout = 15000,
            //    Password = RedisPassword,//Redis数据库密码
            //    EndPoints = { connection }// connectionString 为IP:Port 如”192.168.2.110:6379”
            //};
            //Redis = ConnectionMultiplexer.Connect(config);
            //Db = Redis.GetDatabase(RedisDb);
            if (string.IsNullOrWhiteSpace(Mode))
            {
                var config = new ConfigurationOptions
                {
                    AbortOnConnectFail = false,
                    AllowAdmin = true,
                    ConnectTimeout = 15000,
                    SyncTimeout = 15000,
                    Ssl = false,
                    //ResponseTimeout = 15000,
                    Password = RedisPassword,//Redis数据库密码
                    EndPoints = { connection }// connectionString 为IP:Port 如”192.168.2.110:6379”
                };
                Redis = ConnectionMultiplexer.Connect(config);
                Db = Redis.GetDatabase(RedisDb);
            }
            else
            {
                ConfigurationOptions sentinelOptions = new ConfigurationOptions();
                Logger.Info("Redis配置：" + RedisServerName);
                Logger.Info("Redis配置：" + RedisPassword);
                var address = Address.Split(";");
                foreach (var item in address)
                {
                    sentinelOptions.EndPoints.Add(item);
                }
                sentinelOptions.TieBreaker = "";
                sentinelOptions.CommandMap = CommandMap.Sentinel;
                sentinelOptions.AbortOnConnectFail = false;
                // Connect!
                ConnectionMultiplexer sentinelConnection = ConnectionMultiplexer.Connect(sentinelOptions);

                // Get a connection to the master
                ConfigurationOptions redisServiceOptions = new ConfigurationOptions();
                redisServiceOptions.ServiceName = RedisServerName;   //master名称:mymaster
                redisServiceOptions.Password = RedisPassword;     //master访问密码:bzlrobot
                redisServiceOptions.AbortOnConnectFail = true;
                ConnectionMultiplexer masterConnection = sentinelConnection.GetSentinelMasterConnection(redisServiceOptions);

                Db = masterConnection.GetDatabase(RedisDb);
                Redis = masterConnection;
            }
        }

        /// <summary>
        /// 查询redis key是否存在
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool Exists(string key)
        {
            return Db.KeyExists(key);
        }

        /// <summary>
        /// 查询redis key是否存在
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public async ValueTask<bool> ExistsAsync(string key)
        {
            return await Db.KeyExistsAsync(key);
        }

        /// <summary>
        /// 设置Key的有效期
        /// </summary>
        /// <param name="key"></param>
        /// <param name="time"></param>
        /// <returns></returns>
        public bool Expire(string key, TimeSpan time)
        {
            return Db.KeyExpire(key, time);
        }

        /// <summary>
        /// 增加/修改
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool SetValue(string key, string value)
        {
            return Db.StringSet(key, value, expiry: GetCommonTimeSpan());
        }

        /// <summary>
        /// 增加/修改
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="timeSpan"></param>
        /// <param name="isSliding">redis中无效</param>
        /// <returns></returns>
        public bool SetValue(string key, string value, TimeSpan timeSpan, bool isSliding = false)
        {
            return Db.StringSet(key, value, expiry: timeSpan);
        }

        public async Task<bool> SetValueAsync(string key, string value, TimeSpan timeSpan, bool isSliding = false)
        {
            RedisKey redisKey = AddKeyPrefix(key);
            return await Db.StringSetAsync(redisKey, value, expiry: timeSpan);
        }

        public async Task<bool> SetAsync<T>(string key, T value, TimeSpan expiresSliding)
        {
            if (key == null)
                throw new ArgumentNullException(nameof(key));
            if (value == null)
                throw new ArgumentNullException(nameof(value));
            return await Db.StringSetAsync(key, value.ToJsonStr(), expiresSliding);
        }

        /// <summary>
        /// 暂时不做配置
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        private static string AddKeyPrefix(string key)
        {
            return $"{key}";
        }

        /// <summary>
        /// 根据key获取缓存对象
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<string> GetValueAsync(string key)
        {
            RedisKey redisKey = AddKeyPrefix(key);
            return await Db.StringGetAsync(redisKey);
        }

        public async Task<T> GetAsync<T>(string key)
        {
            RedisKey redisKey = AddKeyPrefix(key);
            string jsonStr = await Db.StringGetAsync(redisKey);
            if (string.IsNullOrEmpty(jsonStr))
                return default(T);
            return jsonStr.ToObject<T>();
        }

        /// <summary>
        /// 查询
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public string GetValue(string key)
        {
            return Db.StringGet(key);
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool DeleteKey(string key)
        {
            return Db.KeyDelete(key);
        }

        /// <summary>
        /// 返回所有匹配前缀的key
        /// </summary>
        /// <param name="endPoint">Network address</param>
        /// <param name="prefix">String key pattern</param>
        /// <returns>List of cache keys</returns>
        public IEnumerable<RedisKey> GetKeys(EndPoint endPoint, string prefix = null)
        {
            var server = Redis.GetServer(endPoint);

            var keys = server.Keys(Db.Database, string.IsNullOrEmpty(prefix) ? null : $"{prefix}*");

            return keys;
        }

        /// <summary>
        /// 获取指定key
        /// </summary>
        /// <param name="prefix"></param>
        /// <returns></returns>
        public IEnumerable<RedisKey> GetKeys(string prefix = null)
        {
            var endPoints = Redis.GetEndPoints();

            var server = Redis.GetServer(endPoints[0]);

            var keys = server.Keys(Db.Database, string.IsNullOrEmpty(prefix) ? null : $"{prefix}*");

            return keys;
        }

        /// <summary>
        /// 移出所有匹配Key前缀的项
        /// </summary>
        /// <param name="prefix">String key prefix</param>
        public void RemoveByPrefix(string prefix)
        {
            foreach (var endPoint in Redis.GetEndPoints())
            {
                var keys = GetKeys(endPoint, prefix);

                Db.KeyDelete(keys.ToArray());
            }
        }

        public void HashSet(string key, HashEntry[] hashEntiry)
        {
            //操作数据库0
            Db.HashSet(key, hashEntiry);
        }

        /// <summary>
        /// 把hash表值插入到redis
        /// </summary>
        /// <param name="key">Hash表key</param>
        /// <param name="hashEntiry">Hash表值</param>
        /// <returns></returns>
        public async Task HashSetAsync(string key, HashEntry[] hashEntiry)
        {
            //操作数据库0
            await Db.HashSetAsync(key, hashEntiry);
        }

        /// <summary>
        /// 删除指定hash表的值
        /// </summary>
        /// <param name="key">Hash表key</param>
        /// <param name="redisValue">Hash某字段名称</param>
        /// <returns></returns>
        public async Task<bool> HashDeleteAsync(string key, RedisValue redisValue)
        {
            //操作数据库0
            return await Db.HashDeleteAsync(key, redisValue);
        }

        /// <summary>
        /// 删除指定hash表的值
        /// </summary>
        /// <param name="key">Hash表key</param>
        /// <param name="redisValue">Hash某字段名称</param>
        /// <returns></returns>
        public async Task<bool> StringDeleteAsync(string key)
        {
            //操作数据库0
            return await Db.KeyDeleteAsync(key);
        }

        public HashEntry[] HashGetAll(string key)
        {
            //操作数据库0
            return Db.HashGetAll(key);
        }

        /// <summary>
        /// 根据key和field获取hash的值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="field"></param>
        /// <returns></returns>
        public string HashGet(string key, string field)
        {
            return Db.HashGet(key, field);
        }

        /// <summary>
        /// 返回hash表所有的值
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<HashEntry[]> HashGetAllAsync(string key)
        {
            //操作数据库0
            return await Db.HashGetAllAsync(key);
        }

        /// <summary>
        /// 获取hash表的字段长度
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<long> HashLengthAsync(string key)
        {
            //操作数据库0
            return await Db.HashLengthAsync(key);
        }

        /// <summary>
        /// 判断hash表是否包含该字段
        /// </summary>
        /// <param name="key">哈希表</param>
        /// <param name="redisValue">字段</param>
        /// <returns></returns>
        public async Task<bool> HashExistsAsync(string key, RedisValue redisValue)
        {
            //操作数据库0
            return await Db.HashExistsAsync(key, redisValue);
        }

        public bool LockTake(string lockKey, RedisValue redisValue, TimeSpan timespan)
        {
            return Db.LockTake(lockKey, redisValue, timespan);
        }

        /// <summary>
        /// 获取分布式锁，并且执行
        /// </summary>
        /// <param name="lockKey">所名称</param>
        /// <param name="redisValue"></param>
        /// <param name="timespan"></param>
        /// <returns></returns>
        public async Task<bool> LockTakeAsync(string lockKey, RedisValue redisValue, TimeSpan timespan)
        {
            return await Db.LockTakeAsync(lockKey, redisValue, timespan);
        }

        /// <summary>
        /// 释放分布式锁
        /// </summary>
        /// <param name="lockKey"></param>
        /// <param name="redisValue"></param>
        /// <returns></returns>
        public bool LockRelease(string lockKey, RedisValue redisValue)
        {
            return Db.LockRelease(lockKey, redisValue);
        }

        /// <summary>
        /// 释放分布式锁
        /// </summary>
        /// <param name="lockKey"></param>
        /// <param name="redisValue"></param>
        /// <returns></returns>
        public async Task<bool> LockReleaseAsync(string lockKey, RedisValue redisValue)
        {
            return await Db.LockReleaseAsync(lockKey, redisValue);
        }

        /// <summary>
        /// 清理所有的缓存数据
        /// </summary>
        public virtual void Clear()
        {
            foreach (var endPoint in Redis.GetEndPoints())
            {
                var keys = GetKeys(endPoint).ToArray();

                Db.KeyDelete(keys);
            }
        }

        public virtual async Task<long> LPushAsync(String key, String value) => await
            Db.ListLeftPushAsync(key, value);

        public virtual async Task<long> RPush(String key, String value) => await
            Db.ListRightPushAsync(key, value);

        public int[] GetIntList(string key)
        {
            var list = Db.ListRange(key);
            return list?.Select(p => (int)p)?.ToArray();
        }

        public void InsertIntToList(string key, int value)
        {
            //var list = Db.ListRange(key);
            //if (list == null || list.Count(x => x == value) < 1)
            Db.ListRightPush(key, value);
        }
    }
}